package player

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"

	log "github.com/sirupsen/logrus"

	"GameServer/Common/data"
	"GameServer/Common/utils"
	"GameServer/Game/active_user"
	"GameServer/Game/energy_quick"
	"GameServer/Game/errors"
	"GameServer/Game/lib"
	"GameServer/Game/m"
	"GameServer/Game/plog"
	"GameServer/Game/slot_fire"
	"GameServer/Game/sp"
	"GameServer/Game/union"
	protocol "GameServer/Protocol"
	"GameServer/Protocol/protocol/sproto"
)

type Player struct {
	User         *m.User
	client       *protocol.Client
	mdata        *sync.Map
	rwFlag       int64
	otherLogin   bool  // 其他设备登录标记
	closureLogin bool  // 封禁登录
	platform     int64 // 登录平台 1:游客登录 2:google登录 3:facebook登录 5:naver登录 6:华为登录 7:IOS登录
}

func NewPlayer() *Player {
	return &Player{mdata: new(sync.Map)}
}

func (p *Player) GetData(key interface{}) (val interface{}, ok bool) {
	val, ok = p.mdata.Load(key)
	return
}

func (p *Player) PutData(key interface{}, val interface{}) {
	p.mdata.Store(key, val)
}

func (p *Player) DeleteData(key interface{}) {
	p.mdata.Delete(key)
}

func (p *Player) OnConnect(client *protocol.Client) bool {
	p.client = client
	p.User = new(m.User)
	if p.otherLogin {
		fmt.Printf("loginRequest--> otherLogin ip: %s \n", p.client.GetAddrString())
		return false
	}
	return true
}

func (p *Player) OnMessage(client *protocol.Client, msg interface{}) {
	if p.closureLogin {
		log.WithFields(log.Fields{
			"uid":    p.User.Uid.Hex(),
			"method": "OnMessage",
			"ip":     p.client.GetAddrString(),
		}).Debug("玩家被封禁")
		return
	}
	if p.otherLogin {
		log.WithFields(log.Fields{
			"uid":    p.User.Uid.Hex(),
			"method": "OnMessage",
			"ip":     p.client.GetAddrString(),
		}).Debug("玩家在其它设备登录")
		return
	}

	// 获取协议
	spMsg := msg.(sproto.Sproto)
	// 获取方法名
	methodName := strings.Title(spMsg.GetTypeName())

	// 给方法加上Request
	if spMsg.GetMode() == sproto.RequestModel {
		methodName += "Request"
	}
	if spMsg.GetMode() == sproto.ResponseModel {
		methodName += "Response"
	}

	// 创建方法参数
	in := make([]reflect.Value, 1)
	in[0] = reflect.ValueOf(msg)
	t := reflect.ValueOf(p)

	log.WithFields(log.Fields{
		"uid":    p.User.Uid.Hex(),
		"method": methodName,
		"type":   spMsg.GetType(),
		"data":   msg,
		"ip":     p.client.GetAddrString(),
	}).Debug("OnMessage")

	// 反射创建方法
	method := t.MethodByName(methodName)
	if method.IsValid() {
		// 调用这个方法
		res := method.Call(in)
		for _, itemRes := range res {
			val := itemRes.Interface()
			if val != nil {
				switch val.(type) {
				case error:
					log.Error("err:", val)
					return
				default:
					if !itemRes.IsNil() {
						// 给客户端发送协议
						p.Send(val)
					}
				}
			}
		}
	} else {
		log.Error("not function : ", methodName)
	}
}

func (p *Player) OnClose(client *protocol.Client) {
	if p.otherLogin || p.closureLogin {
		return
	}

	log.WithFields(log.Fields{
		"uid":  p.User.Uid.Hex(),
		"name": p.User.Name,
		"ip":   p.client.GetAddrString(),
	}).Info("OnClose")

	p.client = nil
	if p.User.Uid.Hex() != "" {
		u := p.User
		err := u.Save()
		if err != nil {
			log.WithFields(log.Fields{
				"uid":  u.Uid.Hex(),
				"name": u.Name,
			}).Error(err)
		}

		PlayersModule.Offline(u)
		// 移除玩家的大富豪
		p.RemoveSlotUser()
		// 玩家离线
		utils.AsyncFunc(func() {
			active_user.UserOffLine(u)
		})
	}
}

func (p *Player) FindUidByNameRequest(req *sp.FindUidByNameRequest) (res *sp.FindUidByNameResponse, err error) {
	uidGroup, err := lib.FindUidByName(req.Name)
	if err != nil {
		res = &sp.FindUidByNameResponse{
			Errcode: errors.ErrCode(err),
		}
		return res, err
	}
	res = &sp.FindUidByNameResponse{
		Uid: uidGroup,
	}
	return
}

func (p *Player) CploginRequest(req *sp.CploginRequest) (res *sp.CploginResponse, err error) {
	r, err := lib.AuthLogin(req.AccountId, req.Token, req.Platform)
	if err != nil {
		res = &sp.CploginResponse{
			Errcode: errors.ErrCode(err),
		}
		return
	}

	if r {
		uid, err := lib.FindUidByAccountId(req.AccountId)
		if err != nil {
			res = &sp.CploginResponse{
				Errcode: errors.ErrCode(err),
			}
			return res, err
		}
		res = &sp.CploginResponse{
			Uid: uid,
		}
		lib.SetUserPush(uid, req.PushToken, req.Platform)
	}
	p.platform = req.Platform
	return
}

func (p *Player) LoginRequest(req *sp.LoginRequest) (res *sp.LoginResponse, err error) {
	p1, ok := PlayersModule.GetPlayer(req.Uid)
	if !ok {
		// @todo 方便测试 名字直接可以登录
		//p1, ok = PlayersModule.GetPlayerByName(req.Name)//关闭，根据玩家名称，二次搜索入口
		err = errors.NewErrcode(data.Errcode.UserNotFound, "没有该玩家")
		res = &sp.LoginResponse{
			Errcode: data.Errcode.UserNotFound,
		}
		return
	}

	if p1 == nil {
		return
	}
	p1.otherLogin = false
	p1.closureLogin = false
	us := p1.User
	// 如果已经登录 则断开连接

	if p1.client != nil {
		log.WithFields(log.Fields{
			"uid":    p1.User.Uid.Hex(),
			"method": "LoginRequest",
		}).Debug("多点登录，断开之前老玩家")
		p1.Send(&sp.OrtherLoginRequest{})
		p1.otherLogin = true
		// PlayersModule.Offline(p1.User)
		// 移除老虎机占用对象
		p1.RemoveSlotUser()
		go func() {
			time.Sleep(time.Second)
			if p1.client != nil {
				p1.client.Close()
			}
		}()
	}

	now := utils.TNow()
	// 账号被封禁，禁止登陆
	if now.Unix() < us.ClosureEndTime {
		res = &sp.LoginResponse{
			Errcode: data.Errcode.ClosureLogin,
			Info:    lib.FormatBanUserInfoSp(us),
		}
		err = errors.New("账号被封禁，禁止登陆")
		return
	}

	rEnergy := energy_quick.Module.GetRestoreEnergy()
	us.Init(rEnergy)

	us.LangsTyp = req.LangsTyp
	if p.platform > 0 {
		us.Platform = p.platform
	}

	p.User = us

	uNewGuide := us.GetNewGuide()
	// 玩家通过新手引导，可以推送活动。
	if uNewGuide.SlotFlag {
		p.PushAllActActivityInfo() // 所有活动信息
	}
	p.ScryerInfoPush()           // 占卜信息
	p.PushUserSpBoxInfo()        // 特殊箱子信息
	p.NewGuideInfoPushToPlayer() // 推送新手引导数据
	if p.User.AvatarFrame == 0 {
		p.User.SetDefaultAvatarFrame()
	}

	res = &sp.LoginResponse{
		Info: lib.FormatUserInfoSp(us),
	}
	res.Info.SlotUser = p.AddSlotUser()
	//fmt.Printf("Login simpleUser = %s", res.Info.SlotUser.Name)
	PlayersModule.Online(p)
	utils.AsyncFunc(func() {
		p.UnionRecvEnergyNotice()
		p.GiveTestActivityRes()
	})

	utils.AsyncFunc(func() {
		// 记录玩家登录时间
		plog.Log_UserDailyLog(us)
		// 记录玩家登录地址
		plog.Log_UserAddrLog(us, p.client.GetAddrString())
		// 有委托任务完成。
		if num := lib.EntrustCheck(us); num > 0 {
			p.Send(&sp.EntrustNoticeRequest{
				Num: num,
			})
		}
	})

	utils.AsyncFunc(func() {
		p.GoldEffectSlotMatchPool()
		union.UnionModule.OnLine(us.UnionId, us.Uid.Hex(), BroadData)
	})

	// 玩家上线
	utils.AsyncFunc(func() {
		active_user.UserOnLine(us)
	})
	utils.AsyncFunc(func() { // 保存数据会有5秒钟以上，放入协程中，减少注册时间
		us.Save() // 用户登录保存一次信息，保证基本信息
	})
	return
}

func (p *Player) RegisterRequest(req *sp.RegisterRequest) (res *sp.RegisterResponse, err error) {

	if p.User != nil && p.User.Uid.Hex() != "" {
		res = &sp.RegisterResponse{}
		return
	}

	// 原子操作，对注册玩家的数量增加1。
	atomic.AddInt64(&PlayerCount, 1)

	u, err := lib.Register(req.Name, req.Head, req.AccountId, req.FacebookId, req.Platform, req.LangsTyp, PlayerCount)
	if err != nil {
		res = &sp.RegisterResponse{
			Errcode: errors.ErrCode(err),
		}
		return
	}
	utils.AsyncFunc(func() { // 保存数据会有5秒钟以上，放入协程中，减少注册时间
		u.Save() // 注册用户赋值基本信息后，先执行一次保存
	})

	p.platform = req.Platform
	p.User = u
	log.WithFields(log.Fields{
		"Uid":      p.User.Uid.Hex(),
		"Name":     p.User.Name,
		"Platform": p.platform,
		"method":   "RegisterRequest",
	}).Info("注册平台...")

	// p.PlatformAvatarFrameRes()   // 头像框奖励检测
	// p.PushAllActActivityInfo()   // 所有活动信息
	p.NewGuideInfoPushToPlayer() // 推送新手引导数据
	res = &sp.RegisterResponse{
		Info: lib.FormatUserInfoSp(u),
	}
	res.Info.SlotUser = p.AddSlotUser()
	utils.AsyncFunc(func() {
		PlayersModule.Online(p)
	})
	utils.AsyncFunc(func() {
		slot_fire.AddFireUser(p.User.Uid.Hex()) // 用户注册，添加老虎机放火匹配池。
	})
	utils.AsyncFunc(func() {
		// p.GiveTestActivityRes()
		p.PlatformRes()
	})

	utils.AsyncFunc(func() {
		plog.Log_UserDailyLog(p.User)                          //记录玩家登录时间
		plog.Log_UserAddrLog(p.User, p.client.GetAddrString()) // 记录玩家登录地址
	})
	return
}

// 10005 修改名字和头像
func (p *Player) ChangeNameAndHeadRequest(req *sp.ChangeNameAndHeadRequest) (res *sp.ChangeNameAndHeadResponse) {
	if p.User == nil || p.User.Uid.Hex() == "" {
		res = &sp.ChangeNameAndHeadResponse{
			Errcode: data.Errcode.UserNotFound,
		}
		return
	}
	if req.Name == "" || req.Head == "" {
		res = &sp.ChangeNameAndHeadResponse{
			Errcode: data.Errcode.ServerErr,
		}
		log.WithFields(log.Fields{
			"uid":     p.User.Uid.Hex(),
			"Name":    p.User.Name,
			"NewName": req.Name,
			"NewHead": req.Head,
			"method":  "ChangeNameAndHeadRequest",
		}).Debug("换名和头像失败,名字或头像为空")
		return
	}
	us := p.User

	nowTime := utils.TNow().Unix()
	if req.NeedCD == 1 {
		if us.RenameCd > nowTime { // 1、改名需要CD，CD时间大于现在时间，不能改名

		} else { // 2、改名需要CD，CD时间 小于 现在时间，可以改名
			us.RenameCd = utils.TNow().Add(1440 * time.Hour).Unix() // 改名CD 60天
			us.ChangeNameAndHead(req.Name, req.Head)
			p.PlayerNameAndHeadChange()
		}
	} else { // 3、改名不需要CD，随便改名
		us.ChangeNameAndHead(req.Name, req.Head)
		p.PlayerNameAndHeadChange()
	}

	res = &sp.ChangeNameAndHeadResponse{
		Name:     req.Name,
		Head:     req.Head,
		RenameCd: us.RenameCd,
	}
	return
}

// 10007 绑定社交账号
func (p *Player) BindSocialRequestRequest(req *sp.BindSocialRequestRequest) (res *sp.BindSocialRequestResponse) {
	if p.User == nil || p.User.Uid.Hex() == "" {
		res = &sp.BindSocialRequestResponse{
			Errcode: data.Errcode.UserNotFound,
		}
		return
	}
	newPlatForm := req.NewPlatform
	if p.platform != newPlatForm {
		if newPlatForm == 3 { //FaceBook
			p.platform = 3
			p.PlatformAvatarFrameRes() // 头像框奖励
			utils.AsyncFunc(func() {
				p.PlatformRes() //绑定奖励
			})
		}
	}
	p.User.ChangePlatformAndNameHead(newPlatForm, req.Name, req.Head, req.PlatformId)
	res = &sp.BindSocialRequestResponse{
		Errcode: 0,
		Name:    p.User.Name,
		Head:    p.User.Head,
	}
	return
}

// 10008 更改头像框
func (p *Player) ChangeAvatarFrameRequest(req *sp.ChangeAvatarFrameRequest) (res *sp.ChangeAvatarFrameResponse) {
	if p.User == nil || p.User.Uid.Hex() == "" {
		res = &sp.ChangeAvatarFrameResponse{
			Errcode: data.Errcode.UserNotFound,
		}
		return
	}
	p.User.ChangeAvatarFrame(req.FrameId)
	res = &sp.ChangeAvatarFrameResponse{
		Errcode:        0,
		AvatarFrame:    p.User.AvatarFrame,
		AvatarFrameArr: lib.FormatAvatarFramesArr(p.User.AvatarFrameArr),
	}
	p.PlayerAvatarFrameChange()
	return
}

func (p *Player) RestoreEnergyRequest(req *sp.RestoreEnergyRequest) (res *sp.RestoreEnergyResponse) {
	if utils.TNow().Unix()-p.User.EnergyTime < p.User.NextRecoverTime || atomic.LoadInt64(&p.rwFlag) > 0 {
		res = &sp.RestoreEnergyResponse{
			Errcode: data.Errcode.ServerErr,
		}
		return
	}

	if atomic.LoadInt64(&p.rwFlag) == 0 {
		atomic.StoreInt64(&p.rwFlag, 1)
		defer atomic.StoreInt64(&p.rwFlag, 0)
		if p.User.Energy >= 50 { //当前体力>=50，不做体力恢复
			return
		}
		//需恢复的体力
		rEnergy := energy_quick.Module.GetRestoreEnergy()
		if p.User.Energy+rEnergy > 50 { //当前体力+rEnergy>50，恢复体力是50-当前体力
			rEnergy = 50 - p.User.Energy
		}
		//当前体力+rEnergy<=50，恢复体力+rEnergy
		p.User.EnergyTime = p.User.EnergyTime + p.User.NextRecoverTime
		uProp, _, _, err := lib.GiveGoodsByKv("RestoreEnergyRequest", p.User, m.BaseKV{Key: data.GlobalEnum.Energy, Val: rEnergy}, p.Send)
		if err != nil {
			res = &sp.RestoreEnergyResponse{
				Errcode: errors.ErrCode(err),
			}
			return
		}
		// 有成就完成，进行通知
		achLst := lib.AchievComplete(p.User)
		if len(achLst) > 0 {
			p.Send(&sp.AchievementNoticeRequest{
				Lst: achLst,
			})
		}
		p.TimelyUpdateProp(req.GetType(), uProp)
		utils.AsyncFunc(func() {
			p.UpdateProp(req.GetType(), uProp)
		})
		res = &sp.RestoreEnergyResponse{}
		return
	}
	return
}

// 10012 用户语言修改
func (p *Player) LanguageChangeRequest(req *sp.LanguageChangeRequest) (res *sp.LanguageChangeResponse) {
	err := lib.LanguageChange(p.User, req.LangsTyp)
	if err != nil {
		res = &sp.LanguageChangeResponse{
			ErrCode: errors.ErrCode(err),
		}
		return
	}
	res = &sp.LanguageChangeResponse{}
	return
}

func (p *Player) UpdateProp(evt uint16, val interface{}) (err error) {
	if val == nil {
		return
	}
	req := new(sp.UpdatePropRequest)
	//添加系统当前时间
	req.Kvs = append(req.Kvs, &sp.BaseKV{Key: data.GlobalEnum.NowTime, Val: utils.TNow().Unix()})
	err = utils.ConvertStructByJson(val, &req)
	if err != nil {
		return
	}

	if req == nil {
		return
	}

	utils.AsyncFunc(func() {
		if req.Kvs == nil || len(req.Kvs) <= 0 {
			return
		}
		for _, item := range req.Kvs {
			if item.Key == data.GlobalEnum.Gold && !((val.(*m.UpdateProp)).SlotNoticeSkip()) {
				p.GoldEffectSlotMatchPool()
				p.UpdateNoticeSlotUser()
			}
			if item.Key == data.GlobalEnum.Star {
				p.StarChange()
			}
			if item.Key == data.GlobalEnum.Union {
				p.UnionScoreChange()
			}
		}
	})

	if p.client == nil {
		return
	}

	req.Evt = int64(evt)
	err = p.Send(req)
	return
}

// 及时向客户端发送的消息，发送的信息被过滤掉。
func (p *Player) TimelyUpdateProp(evt uint16, up *m.UpdateProp) (err error) {
	// fmt.Printf("%+v\n", up)
	if up == nil ||
		up.Kvs == nil ||
		len(up.Kvs) == 0 {
		return
	}
	tup := m.NewUpdateProp()
	kvs := make([]*m.BaseKV, 0)
	// 把及时发送的信息提取出来。
	for _, item := range up.Kvs {
		// 提取盾牌
		if item.Key == data.GlobalEnum.Shield {
			tup.Kvs = append(tup.Kvs, item)
		} else {
			kvs = append(kvs, item)
		}
	}
	// 没有及时发送的信息，不做发送。
	if len(tup.Kvs) == 0 {
		return
	}

	up.Kvs = kvs

	req := new(sp.UpdatePropRequest)
	//添加系统当前时间
	req.Kvs = append(req.Kvs, &sp.BaseKV{Key: data.GlobalEnum.NowTime, Val: utils.TNow().Unix()})
	err = utils.ConvertStructByJson(tup, &req)
	if err != nil {
		return
	}

	if p.client == nil {
		return
	}
	req.Evt = int64(evt)
	err = p.Send(req)
	return
}

func (p *Player) Send(msg interface{}) (err error) {
	if msg == nil {
		return
	}
	if p.client == nil {
		return
	}

	res := msg.(sproto.Sproto)

	log.WithFields(log.Fields{
		"uid":    p.User.Uid.Hex(),
		"type":   res.GetType(),
		"method": res.GetTypeName(),
		"data":   res,
		"ip":     p.client.GetAddrString(),
	}).Debug("send msg")

	err = p.client.WriteMsg(res)
	if err != nil {
		log.Error("send err:", err)
		return
	}
	return
}

func (p *Player) HeartRequest(req *sp.HeartRequest) (res *sp.HeartResponse) {
	nowUnix := utils.TNow().Unix()
	if v, ok := p.GetData("richTime"); ok { // richTime大富豪起始时间
		if nowUnix-v.(int64) > 1200 { // 玩家20分钟后，切换大富豪。
			utils.AsyncFunc(func() {
				p.RefreshSlotUser()
			})
		}
	} else {
		p.PutData("richTime", nowUnix) // 没有大富豪起始时间，就添加当前时间
	}
	res = &sp.HeartResponse{Time: nowUnix}
	return
}

// 平台登录奖励
func (p *Player) PlatformRes() {
	if p.platform == 3 {
		//add平台奖励读表
		tpl, ok := data.Get("FBLoginRewards", uint64(1))
		if !ok {
			log.WithFields(log.Fields{
				"uid":    p.User.Uid.Hex(),
				"method": "PlatformRes",
			}).Error("FBLoginRewards配置表没有找到id:1")
			return
		}
		t := tpl.(data.FBLoginReward)
		var lastIndex int = len(t.Rewards) - 1
		var send string = `[`
		for index, item := range t.Rewards {
			if index == 0 {
				send = `[`
			}
			send += `{"tid":`
			send += strconv.FormatUint(item.Key, 10)
			send += `, "num":`
			send += strconv.FormatUint(item.Value, 10)
			send += `}`
			if index != lastIndex {
				send += `,`
			} else {
				send += `]`
			}
		}
		res := send //`[{"tid":220001, "num":100000}, {"tid":220002, "num":22}, {"tid":220004, "num":10}]`
		err := p.SendMail("Registration Award", "Welcome to the game with your Facebook account.", res)
		if err != nil {
			log.WithFields(log.Fields{
				"uid":    p.User.Uid.Hex(),
				"method": "PlatformRes",
				"type":   "0",
			}).Error("发送邮件失败:", err)
			return
		}
	}
}

// 平台头像框奖励
func (p *Player) PlatformAvatarFrameRes() {
	// if p.platform == 3 {
	// 	p.User.AddAvatarFrame(data.AvatarFrameEnum.PlatformFrame) //FB头像奖励
	// }
}

// 通知用户系统维护 11000
func (p *Player) NoticeMainen(bTime, eTime int64) (err error) {
	req := &sp.NoticeMaintenRequest{
		BeginTime: bTime,
		EndTime:   eTime,
	}
	if p.client == nil {
		return
	}
	err = p.Send(req)
	return
}

// 尝试领取回归奖励 10013
func (p *Player) TryGetReturnRewardRequest(req *sp.TryGetReturnRewardRequest) (res *sp.TryGetReturnRewardResponse) {
	// 读表
	tpl, ok := data.GetAllDatas("ReturnRewards")
	if !ok {
		err := errors.NewErrcode(data.Errcode.ConfigNotFound, "没有找到回归奖励配置,ReturnRewards")
		log.WithFields(log.Fields{
			"err":    err.Error(),
			"method": "GetActActivityList",
		}).Debug("没有找到激活活动配置,ActActivities")
		res = &sp.TryGetReturnRewardResponse{
			ErrCode: errors.ErrCode(err),
		}
		return res
	}
	// leaveTime := p.User.LeaveTime // 回归时间
	// 回归天数
	leaveDay := uint64(p.User.LeaveDay)
	// 找到最大匹配的奖励
	var matchId uint64 = 0
	var matchLeaveDay uint64 = 0
	var matchConfig data.ReturnReward
	if leaveDay > 0 {
		for _, value := range tpl {
			returnConfig := value.(data.ReturnReward)
			if leaveDay >= returnConfig.LeaveDay {
				if returnConfig.LeaveDay > matchLeaveDay {
					matchConfig = returnConfig
					matchId = returnConfig.Id
					matchLeaveDay = returnConfig.LeaveDay
				}
			}
		}
	}
	if matchId > 0 {
		// 准备奖励
		resList := make([]m.BaseKV, 0)
		for _, res := range matchConfig.Rewards {
			resList = append(resList, m.BaseKV{
				Key: int64(res.Key),
				Val: int64(res.Value),
			})
		}
		uProp, resGoods, boxResList, err := lib.GiveGoodsByKvList("TryGetReturnRewardRequest", p.User, resList, p.Send)
		if err != nil {
			res = &sp.TryGetReturnRewardResponse{
				ErrCode: errors.ErrCode(err),
			}
			return res
		}
		res = &sp.TryGetReturnRewardResponse{
			ErrCode: 0,
			Res:     lib.FormatGoodsInfoSps(resGoods),
			BoxRes:  lib.FormatBoxResSps(boxResList),
		}
		p.TimelyUpdateProp(req.GetType(), uProp)
		utils.AsyncFunc(func() {
			p.UpdateProp(req.GetType(), uProp)
		})
		return
	} else {
		// 没有找到合适的奖励
		res = &sp.TryGetReturnRewardResponse{
			ErrCode: 999,
		}
		return res
	}
}
